{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "3db85080-2299-4885-a2f6-fffa6a09a238",
      "metadata": {
        "id": "3db85080-2299-4885-a2f6-fffa6a09a238"
      },
      "source": [
        "# Sub-graphs\n",
        "\n",
        "## Review\n",
        "\n",
        "We're building up to a multi-agent research assistant that ties together all of the modules from this course.\n",
        "\n",
        "We just covered parallelization, which is one important LangGraph controllability topic.\n",
        "\n",
        "## Goals\n",
        "\n",
        "Now, we're [going to cover sub-graphs](https://langchain-ai.github.io/langgraph/how-tos/subgraph/#simple-example).\n",
        "\n",
        "## State\n",
        "\n",
        "Sub-graphs allow you to create and manage different states in different parts of your graph.\n",
        "\n",
        "This is particularly useful for multi-agent systems, with teams of agents that each have their own state.\n",
        "\n",
        "Let's consider a toy example:\n",
        "\n",
        "* I have a system that accepts logs\n",
        "* It performs two separate sub-tasks by different agents (summarize logs, find failure modes)\n",
        "* I want to perform these two operations in two different sub-graphs.\n",
        "\n",
        "The most critical thing to understand is how the graphs communicate!\n",
        "\n",
        "In short, communication is **done with over-lapping keys**:\n",
        "\n",
        "* The sub-graphs can access `docs` from the parent\n",
        "* The parent can access `summary/failure_report` from the sub-graphs\n",
        "\n",
        "![subgraph.png](https://cdn.prod.website-files.com/65b8cd72835ceeacd4449a53/66dbb1abf89f2d847ee6f1ff_sub-graph1.png)\n",
        "\n",
        "## Input\n",
        "\n",
        "Let's define a schema for the logs that will be input to our graph."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 17,
      "id": "2954e8c6-496f-4394-b56a-681608bf65da",
      "metadata": {
        "id": "2954e8c6-496f-4394-b56a-681608bf65da"
      },
      "outputs": [],
      "source": [
        "%%capture --no-stderr\n",
        "%pip install -U  langgraph"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "e7e413ba-e376-4a5f-a666-2d2154aa6fe2",
      "metadata": {
        "id": "e7e413ba-e376-4a5f-a666-2d2154aa6fe2"
      },
      "source": [
        "We'll use [LangSmith](https://docs.smith.langchain.com/) for [tracing](https://docs.smith.langchain.com/concepts/tracing)."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 18,
      "id": "05b26c51",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "05b26c51",
        "outputId": "2bfdef1a-da7b-4891-fafe-ba3d8ed23f1c"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "env: LANGCHAIN_API_KEY=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\n"
          ]
        }
      ],
      "source": [
        "import os\n",
        "from google.colab import userdata\n",
        "\n",
        "%env LANGCHAIN_API_KEY = {userdata.get('LANGCHAIN_API_KEY')}\n",
        "os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
        "os.environ[\"LANGCHAIN_PROJECT\"] = \"langchain-academy\""
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 19,
      "id": "3efaf8bb-f675-4c0b-a575-89c7e2987a33",
      "metadata": {
        "id": "3efaf8bb-f675-4c0b-a575-89c7e2987a33"
      },
      "outputs": [],
      "source": [
        "from operator import add\n",
        "from typing_extensions import TypedDict\n",
        "from typing import List, Optional, Annotated\n",
        "\n",
        "# The structure of the logs\n",
        "class Log(TypedDict):\n",
        "    id: str\n",
        "    question: str\n",
        "    docs: Optional[List]\n",
        "    answer: str\n",
        "    grade: Optional[int]\n",
        "    grader: Optional[str]\n",
        "    feedback: Optional[str]"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "15825627-78c2-4ba0-ad11-95e4afdb771d",
      "metadata": {
        "id": "15825627-78c2-4ba0-ad11-95e4afdb771d"
      },
      "source": [
        "## Sub graphs\n",
        "\n",
        "Here is the failure analysis sub-graph, which uses `FailureAnalysisState`."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 20,
      "id": "f32986a9-6d11-4646-b2c0-fbae4f524579",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 350
        },
        "id": "f32986a9-6d11-4646-b2c0-fbae4f524579",
        "outputId": "d6f94308-ab9e-43dd-daad-0795744b6473"
      },
      "outputs": [
        {
          "data": {
            "image/jpeg": "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",
            "text/plain": [
              "<IPython.core.display.Image object>"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "from IPython.display import Image, display\n",
        "from langgraph.graph import StateGraph, START, END\n",
        "from langgraph.graph.state import CompiledStateGraph\n",
        "\n",
        "# Failure Analysis Sub-graph\n",
        "class FailureAnalysisState(TypedDict):\n",
        "    cleaned_logs: List[Log]\n",
        "    failures: List[Log]\n",
        "    fa_summary: str\n",
        "    processed_logs: List[str]\n",
        "\n",
        "class FailureAnalysisOutputState(TypedDict):\n",
        "    fa_summary: str\n",
        "    processed_logs: List[str]\n",
        "\n",
        "def get_failures(state):\n",
        "    \"\"\" Get logs that contain a failure \"\"\"\n",
        "    cleaned_logs = state[\"cleaned_logs\"]\n",
        "    failures = [log for log in cleaned_logs if \"grade\" in log]\n",
        "    return {\"failures\": failures}\n",
        "\n",
        "def generate_summary(state):\n",
        "    \"\"\" Generate summary of failures \"\"\"\n",
        "    failures = state[\"failures\"]\n",
        "    # Add fxn: fa_summary = summarize(failures)\n",
        "    fa_summary = \"Poor quality retrieval of Chroma documentation.\"\n",
        "    return {\"fa_summary\": fa_summary, \"processed_logs\": [f\"failure-analysis-on-log-{failure['id']}\" for failure in failures]}\n",
        "\n",
        "fa_builder: StateGraph = StateGraph(FailureAnalysisState, input=FailureAnalysisState, output=FailureAnalysisOutputState)\n",
        "fa_builder.add_node(\"get_failures\", get_failures)\n",
        "fa_builder.add_node(\"generate_summary\", generate_summary)\n",
        "fa_builder.add_edge(START, \"get_failures\")\n",
        "fa_builder.add_edge(\"get_failures\", \"generate_summary\")\n",
        "fa_builder.add_edge(\"generate_summary\", END)\n",
        "\n",
        "graph: CompiledStateGraph = fa_builder.compile()\n",
        "display(Image(graph.get_graph().draw_mermaid_png()))"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "aa83f44c-0bb9-48c6-afec-dad536e608fa",
      "metadata": {
        "id": "aa83f44c-0bb9-48c6-afec-dad536e608fa"
      },
      "source": [
        "Here is the question summarization sub-grap, which uses `QuestionSummarizationState`."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 21,
      "id": "7149000c-ffb6-4834-bd9e-d35b36c524e7",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 350
        },
        "id": "7149000c-ffb6-4834-bd9e-d35b36c524e7",
        "outputId": "11428d56-cb8e-463a-f18f-0a6aab79dddf"
      },
      "outputs": [
        {
          "data": {
            "image/jpeg": "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",
            "text/plain": [
              "<IPython.core.display.Image object>"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "# Summarization subgraph\n",
        "class QuestionSummarizationState(TypedDict):\n",
        "    cleaned_logs: List[Log]\n",
        "    qs_summary: str\n",
        "    report: str\n",
        "    processed_logs: List[str]\n",
        "\n",
        "class QuestionSummarizationOutputState(TypedDict):\n",
        "    report: str\n",
        "    processed_logs: List[str]\n",
        "\n",
        "def generate_summary(state):\n",
        "    cleaned_logs = state[\"cleaned_logs\"]\n",
        "    # Add fxn: summary = summarize(generate_summary)\n",
        "    summary = \"Questions focused on usage of ChatOllama and Chroma vector store.\"\n",
        "    return {\"qs_summary\": summary, \"processed_logs\": [f\"summary-on-log-{log['id']}\" for log in cleaned_logs]}\n",
        "\n",
        "def send_to_slack(state):\n",
        "    qs_summary = state[\"qs_summary\"]\n",
        "    # Add fxn: report = report_generation(qs_summary)\n",
        "    report = \"foo bar baz\"\n",
        "    return {\"report\": report}\n",
        "\n",
        "qs_builder: StateGraph = StateGraph(state_schema=QuestionSummarizationState, input=QuestionSummarizationState, output=QuestionSummarizationOutputState)\n",
        "qs_builder.add_node(\"generate_summary\", generate_summary)\n",
        "qs_builder.add_node(\"send_to_slack\", send_to_slack)\n",
        "qs_builder.add_edge(START, \"generate_summary\")\n",
        "qs_builder.add_edge(\"generate_summary\", \"send_to_slack\")\n",
        "qs_builder.add_edge(\"send_to_slack\", END)\n",
        "\n",
        "graph: CompiledStateGraph = qs_builder.compile()\n",
        "display(Image(graph.get_graph().draw_mermaid_png()))"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f10a5baf-beab-4927-807a-3e6a5ad3d202",
      "metadata": {
        "id": "f10a5baf-beab-4927-807a-3e6a5ad3d202"
      },
      "source": [
        "## Adding sub graphs to our parent graph\n",
        "\n",
        "Now, we can bring it all together.\n",
        "\n",
        "We create our parent graph with `EntryGraphState`.\n",
        "\n",
        "And we add our sub-graphs as nodes!\n",
        "\n",
        "```\n",
        "entry_builder.add_node(\"question_summarization\", qs_builder.compile())\n",
        "entry_builder.add_node(\"failure_analysis\", fa_builder.compile())\n",
        "```"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 22,
      "id": "587c8fe1-1ae8-411e-a55d-cac299026646",
      "metadata": {
        "id": "587c8fe1-1ae8-411e-a55d-cac299026646"
      },
      "outputs": [],
      "source": [
        "# Entry Graph\n",
        "class EntryGraphState(TypedDict):\n",
        "    raw_logs: List[Log]\n",
        "    cleaned_logs: Annotated[List[Log], add] # This will be USED BY in BOTH sub-graphs\n",
        "    fa_summary: str # This will only be generated in the FA sub-graph\n",
        "    report: str # This will only be generated in the QS sub-graph\n",
        "    processed_logs:  Annotated[List[int], add] # This will be generated in BOTH sub-graphs"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5d4da397-310c-4453-969a-e0ae2cc75db8",
      "metadata": {
        "id": "5d4da397-310c-4453-969a-e0ae2cc75db8"
      },
      "source": [
        "But, why does `cleaned_logs` have a reducer if it only goes *into* each sub-graph as an input? It is not modified.\n",
        "\n",
        "```\n",
        "cleaned_logs: Annotated[List[Log], add] # This will be USED BY in BOTH sub-graphs\n",
        "```\n",
        "\n",
        "This is because the output state of the subgraphs will contain **all keys**, even if they are unmodified.\n",
        "\n",
        "The sub-graphs are run in parallel.\n",
        "\n",
        "Because the parallel sub-graphs return the same key, it needs to have a reducer like `operator.add` to combine the incoming values from each sub-graph.\n",
        "\n",
        "But, we can work around this by using another concept we talked about before.\n",
        "\n",
        "We can simply create an output state schema for each sub-graph and ensure that the output state schema contains different keys to publish as output.\n",
        "\n",
        "We don't actually need each sub-graph to output `cleaned_logs`."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 23,
      "id": "50092b9b-70c1-41b1-a74a-254683e28ce0",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 499
        },
        "id": "50092b9b-70c1-41b1-a74a-254683e28ce0",
        "outputId": "03a9c47b-63cb-4fdb-be29-117c96a9db1b"
      },
      "outputs": [
        {
          "data": {
            "image/jpeg": "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",
            "text/plain": [
              "<IPython.core.display.Image object>"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "# Entry Graph\n",
        "class EntryGraphState(TypedDict):\n",
        "    raw_logs: List[Log]\n",
        "    cleaned_logs: List[Log]\n",
        "    fa_summary: str # This will only be generated in the FA sub-graph\n",
        "    report: str # This will only be generated in the QS sub-graph\n",
        "    processed_logs:  Annotated[List[int], add] # This will be generated in BOTH sub-graphs\n",
        "\n",
        "def clean_logs(state):\n",
        "    # Get logs\n",
        "    raw_logs = state[\"raw_logs\"]\n",
        "    # Data cleaning raw_logs -> docs\n",
        "    cleaned_logs = raw_logs\n",
        "    return {\"cleaned_logs\": cleaned_logs}\n",
        "\n",
        "entry_builder: StateGraph = StateGraph(EntryGraphState)\n",
        "entry_builder.add_node(\"clean_logs\", clean_logs)\n",
        "entry_builder.add_node(\"question_summarization\", qs_builder.compile())\n",
        "entry_builder.add_node(\"failure_analysis\", fa_builder.compile())\n",
        "\n",
        "entry_builder.add_edge(START, \"clean_logs\")\n",
        "entry_builder.add_edge(\"clean_logs\", \"failure_analysis\")\n",
        "entry_builder.add_edge(\"clean_logs\", \"question_summarization\")\n",
        "entry_builder.add_edge(\"failure_analysis\", END)\n",
        "entry_builder.add_edge(\"question_summarization\", END)\n",
        "\n",
        "graph: CompiledStateGraph = entry_builder.compile()\n",
        "\n",
        "from IPython.display import Image, display\n",
        "\n",
        "# Setting xray to 1 will show the internal structure of the nested graph\n",
        "display(Image(graph.get_graph(xray=1).draw_mermaid_png()))"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 24,
      "id": "17af1254-4e75-4349-9a79-295f4ec95016",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "17af1254-4e75-4349-9a79-295f4ec95016",
        "outputId": "1a9f548c-9bcb-4c7c-94c4-3e5f8aaa2276"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "{'raw_logs': [{'id': '1',\n",
              "   'question': 'How can I import ChatOllama?',\n",
              "   'answer': \"To import ChatOllama, use: 'from langchain_community.chat_models import ChatOllama.'\"},\n",
              "  {'id': '2',\n",
              "   'question': 'How can I use Chroma vector store?',\n",
              "   'answer': 'To use Chroma, define: rag_chain = create_retrieval_chain(retriever, question_answer_chain).',\n",
              "   'grade': 0,\n",
              "   'grader': 'Document Relevance Recall',\n",
              "   'feedback': 'The retrieved documents discuss vector stores in general, but not Chroma specifically'}],\n",
              " 'cleaned_logs': [{'id': '1',\n",
              "   'question': 'How can I import ChatOllama?',\n",
              "   'answer': \"To import ChatOllama, use: 'from langchain_community.chat_models import ChatOllama.'\"},\n",
              "  {'id': '2',\n",
              "   'question': 'How can I use Chroma vector store?',\n",
              "   'answer': 'To use Chroma, define: rag_chain = create_retrieval_chain(retriever, question_answer_chain).',\n",
              "   'grade': 0,\n",
              "   'grader': 'Document Relevance Recall',\n",
              "   'feedback': 'The retrieved documents discuss vector stores in general, but not Chroma specifically'}],\n",
              " 'fa_summary': 'Poor quality retrieval of Chroma documentation.',\n",
              " 'report': 'foo bar baz',\n",
              " 'processed_logs': ['summary-on-log-1',\n",
              "  'summary-on-log-2',\n",
              "  'failure-analysis-on-log-2']}"
            ]
          },
          "execution_count": 24,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Dummy logs\n",
        "question_answer = Log(\n",
        "    id=\"1\",\n",
        "    question=\"How can I import ChatOllama?\",\n",
        "    answer=\"To import ChatOllama, use: 'from langchain_community.chat_models import ChatOllama.'\",\n",
        ")\n",
        "\n",
        "question_answer_feedback = Log(\n",
        "    id=\"2\",\n",
        "    question=\"How can I use Chroma vector store?\",\n",
        "    answer=\"To use Chroma, define: rag_chain = create_retrieval_chain(retriever, question_answer_chain).\",\n",
        "    grade=0,\n",
        "    grader=\"Document Relevance Recall\",\n",
        "    feedback=\"The retrieved documents discuss vector stores in general, but not Chroma specifically\",\n",
        ")\n",
        "\n",
        "raw_logs = [question_answer, question_answer_feedback]\n",
        "graph.invoke({\"raw_logs\": raw_logs})"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "9192d228-4d3d-4fb0-8bea-26772c3d2e0b",
      "metadata": {
        "id": "9192d228-4d3d-4fb0-8bea-26772c3d2e0b"
      },
      "source": [
        "## LangSmith\n",
        "\n",
        "Let's look at the LangSmith trace:\n",
        "\n",
        "https://smith.langchain.com/public/f8f86f61-1b30-48cf-b055-3734dfceadf2/r"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 24,
      "id": "qceJJDKZVNdn",
      "metadata": {
        "id": "qceJJDKZVNdn"
      },
      "outputs": [],
      "source": []
    }
  ],
  "metadata": {
    "colab": {
      "provenance": []
    },
    "kernelspec": {
      "display_name": "Python 3 (ipykernel)",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.12.6"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 5
}
